package com.gitee.sop.layui.adminserver.module.background.admin.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.gitee.sop.layui.adminserver.entrance.bo.ServiceContext;
import com.gitee.sop.layui.adminserver.module.background.admin.entity.SysApplication;
import com.gitee.sop.layui.adminserver.module.background.admin.entity.SysMenuRole;
import com.gitee.sop.layui.adminserver.module.background.admin.entity.SysUser;
import com.gitee.sop.layui.adminserver.module.background.admin.mapper.SysMenuRoleMapper;
import com.gitee.sop.layui.adminserver.module.background.admin.service.SysDataModifyLockService;
import com.gitee.sop.layui.adminserver.module.background.admin.service.SysMenuRoleRelationService;
import com.gitee.sop.layui.adminserver.module.background.admin.service.SysMenuRoleService;
import com.gitee.sop.layui.adminserver.module.background.admin.service.SysUserMenuRoleRelationService;
import com.gitee.sop.layui.adminserver.module.background.admin.util.SysDataModifyLockUtil;
import com.gitee.sop.layui.adminserver.module.base.bo.ZTreeNode;
import com.gitee.sop.layui.adminserver.module.base.error.ServiceInterruptionException;
import com.gitee.sop.layui.adminserver.module.base.util.AssertUtil;
import com.gitee.sop.layui.adminserver.module.base.util.ObjectUtil;
import com.gitee.sop.layui.adminserver.module.base.util.SqlTreeUtil;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * <p>
 * 角色 服务实现类
 * </p>
 *
 * @author admin
 * @since 2019-11-13
 */
@Service
public class SysMenuRoleServiceImpl extends ServiceImpl<SysMenuRoleMapper, SysMenuRole> implements SysMenuRoleService {

    @Lazy
    @Resource
    private SysMenuRoleRelationService sysMenuRoleRelationService;

    @Lazy
    @Resource
    private SysUserMenuRoleRelationService sysUserMenuRoleRelationService;

    @Lazy
    @Resource
    private SysDataModifyLockService sysDataModifyLockService;

    @Override
    public boolean updateById(SysMenuRole sysMenuRole) {
        SysMenuRole old = getById(sysMenuRole.getId());
        AssertUtil.notNull(old, ServiceInterruptionException.fail(400, "更新数据不存在"));

        sysDataModifyLockService.checkLocked(SysDataModifyLockUtil.generateKey(SysMenuRole.class, old.getUuid()));

        //状态更新，且更新为禁用状态，将子节点全部更新为禁用状态
        if (!old.getStatus().equals(sysMenuRole.getStatus()) && sysMenuRole.getStatus() == 0) {
            SysApplication curApplication = ServiceContext.me().getCurrentApplication();
            baseMapper.updateStatusForChildren(curApplication.getTreeGroup(), curApplication.getTreeLevel(), curApplication.getTreeLeftTraverseNumber(), 0);
        }
        return super.updateById(sysMenuRole);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void add(int parentId, SysMenuRole sysMenuRole) {
        SysMenuRole old = getOneByName(sysMenuRole.getName());
        AssertUtil.isNull(old, ServiceInterruptionException.fail(400, "该角色已存在[" + sysMenuRole.getName() + "]"));

        sysMenuRole.init().setCreatedTime(new Date()).setCreatorUuid(ServiceContext.me().getUser(SysUser.class).getUuid());
        if (ObjectUtil.isEmpty(sysMenuRole.getAppId())) {
            sysMenuRole.setAppId(ServiceContext.me().getCurrentApplication().getAppId());
        }

        if (parentId == 0) {
            sysMenuRole.initTree();
        } else {
            SysMenuRole parent = this.getById(parentId);
            AssertUtil.notNull(parent, ServiceInterruptionException.fail(400, "非法操作"));
            SqlTreeUtil.setInertInfo(parent, sysMenuRole);
            //插入节点需要更树信息，以保持当前树状态
            this.baseMapper.updateTreeInfoAtInsert(parent.getTreeGroup(), parent.getTreeLeftTraverseNumber());
        }

        save(sysMenuRole);
    }

    @Override
    public SysMenuRole getOneByName(String name) {
        return super.getOne(new LambdaQueryWrapper<SysMenuRole>().eq(SysMenuRole::getName, name));
    }

    @Override
    public void deleteSelfAndChildrenById(int id) {
        SysMenuRole sysMenuRole = this.baseMapper.selectById(id);
        if (sysMenuRole == null) {
            return;
        }
        //用户权限校验
        SysUser cur = ServiceContext.me().checkedUserAndGet(SysUser.class);
        AssertUtil.isTrue(cur.getUuid().equals(sysMenuRole.getCreatorUuid()), ServiceInterruptionException.fail(400, "非法操作"));

//
//        //菜单所属应用校验 TODO 判断修改应用菜单用户是否有权修改
//        SysApplication curApplication = ServiceContext.me().getCurrentApplication();
//        AssertUtil.isTrue(curApplication.getAppId().equals(sysMenuRole.getAppId()) ,ServiceInterruptionException.fail(400 ,"非法操作"));

        sysDataModifyLockService.checkLocked(SysDataModifyLockUtil.generateKey(SysMenuRole.class, sysMenuRole.getUuid()));

        //获取当前节点所有子级节点
        List<SysMenuRole> childList = this.baseMapper.selectChildListTreeBy(sysMenuRole.getTreeGroup(), sysMenuRole.getTreeLevel(), sysMenuRole.getTreeLeftTraverseNumber());
        List<String> uuidList = childList.stream().collect(ArrayList::new, (res, ele) -> res.add(ele.getUuid()), (l, r) -> {
        });
        uuidList.add(sysMenuRole.getUuid());

        //查询菜单角色中是否有关联数据
        int menuRelationCount = sysMenuRoleRelationService.countInMenuRoleUuids(uuidList);
        AssertUtil.isFalse(menuRelationCount > 0, ServiceInterruptionException.fail(400, "该角色或其子角色有关联菜单，无法删除"));

        //查询用户菜单角色关联表是否有关联数据
        int userRelationCount = sysUserMenuRoleRelationService.countInMenuRoleUuids(sysMenuRole.getAppId(), uuidList);
        AssertUtil.isFalse(userRelationCount > 0, ServiceInterruptionException.fail(400, "该角色或其子角色有关联用户，无法删除"));

        //删除自身和所有子节点
        this.baseMapper.deleteBatchByUuids(sysMenuRole.getAppId(), uuidList);

        //更新删除数据后的树结构
        this.baseMapper.updateTreeInfoAtDelete(sysMenuRole.getTreeGroup(), sysMenuRole.getTreeLeftTraverseNumber(), uuidList.size());
    }

    @Override
    public void deleteInUuid(String appId, List<String> menuRoleUuidList) {
        this.baseMapper.deleteBatchByUuids(appId, menuRoleUuidList);
    }

    @Override
    public List<ZTreeNode> searchTreeByUserUuid(String userUuid, String appId) {
        SysApplication curApplication = ServiceContext.me().getCurrentApplication();

        String realAppId = ObjectUtil.isEmpty(appId) ? curApplication.getAppId() : appId;

        List<String> roleUuidList = sysUserMenuRoleRelationService.listRoleUuidByUserUuid(realAppId, userUuid);
        SysUser cur = ServiceContext.me().checkedUserAndGet(SysUser.class);

        List<SysMenuRole> list = baseMapper.selectAllByCreatorUuid(realAppId, cur.getUuid(), 1);

        List<Map<String, Object>> sysMenuRoleTreeList = SqlTreeUtil.tree(list);

        List<ZTreeNode> zTreeNodeList = new ArrayList<>(list.size());
        sysMenuRoleTreeList.forEach(map -> {
            ZTreeNode zTreeNode = new ZTreeNode();
            zTreeNode.setName((String) map.get("name")).setId((Integer) map.get("id")).setPId((Integer) map.get("pid"));
            if (roleUuidList.contains(map.get("uuid"))) {
                zTreeNode.setChecked(true);
            }
            zTreeNodeList.add(zTreeNode);
        });

        zTreeNodeList.add(ZTreeNode.createParent());
        return zTreeNodeList;
    }

    @Override
    public List<Map<String, Object>> getListForCurUser(String appId) {
        SysUser cur = ServiceContext.me().checkedUserAndGet(SysUser.class);
        String realAppId = ObjectUtil.isEmpty(appId) ? ServiceContext.me().getCurrentApplication().getAppId() : appId;
        return SqlTreeUtil.tree(baseMapper.selectAllByCreatorUuid(realAppId, cur.getUuid(), null));
    }

    @Override
    public SysMenuRole getOneByUuid(String uuid) {
        return this.baseMapper.selectOneByUuid(uuid);
    }

    @Override
    public List<SysMenuRole> getListByIds(String appId, List<Integer> idList) {
        SysApplication curApplication = ServiceContext.me().getCurrentApplication();
        String realAppId = ObjectUtil.isEmpty(appId) ? ServiceContext.me().getCurrentApplication().getAppId() : appId;
        return this.baseMapper.selectListByIds(realAppId, idList);
    }

    @Override
    public SysMenuRole getOneBy(String appId, String treeGroup, Integer treeLeftTraverseNumber) {
        return this.baseMapper.selectOneBy(appId, treeGroup, treeLeftTraverseNumber);
    }

    @Override
    public List<SysMenuRole> getListByAppId(String appId) {
        return this.baseMapper.selectByAppId(appId);
    }

}
